/*
 * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.xuexiang.xui_lib.component.imageview.edit;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.Image;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 多点触摸监听
 *
 * @author xuexiang
 * @since 2019-10-28 10:03
 */
class MultiTouchListener implements Component.TouchEventListener, Component.LongClickedListener {
    private static final int INVALID_POINTER_ID = -1;
    private final GestureDetector mGestureListener;
    private boolean isTranslateEnabled = true;
    private int mActivePointerId = INVALID_POINTER_ID;
    //private float mPrevX;
    //private float mPrevY;
    private  float mTranX;
    private  float mTranY;
    private ScaleGestureDetector mScaleGestureDetector;
    private int[] location = new int[2];
    private Rect outRect;
    private Component deleteView;
    private Image photoEditImageView;
    private OnMultiTouchListener onMultiTouchListener;
    private OnGestureControl mOnGestureControl;
    private boolean mIsTextPinchZoomable;
    private OnPhotoEditorListener mOnPhotoEditorListener;
    //private boolean isRotateEnabled = true;
    //private boolean isScaleEnabled = true;
    private float minimumScale = 0.5f;
    private float maximumScale = 10.0f;
    //    private DependentLayout parentView;
    private float scaleX = 1f;
    private float scaleY = 1f;
    // 按下时候手指坐标点
    private MmiPoint[] mPointerStart = new MmiPoint[2];
    // 位移手指的坐标点
    private MmiPoint[] mPointerEnd = new MmiPoint[2];
    // 单指按下时控件的位移距离
    private Float[] componentTranslation = new Float[2];

    MultiTouchListener(Component deleteView, DependentLayout parentView,
                       Image photoEditImageView, boolean isTextPinchZoomable,
                       OnPhotoEditorListener onPhotoEditorListener) {
        mIsTextPinchZoomable = isTextPinchZoomable;
        mScaleGestureDetector = new ScaleGestureDetector(new ScaleGestureListener());
        mGestureListener = new GestureDetector(new GestureListener());
        this.deleteView = deleteView;
        this.photoEditImageView = photoEditImageView;
        this.photoEditImageView.setLongClickedListener(this);
        this.mOnPhotoEditorListener = onPhotoEditorListener;
        if (deleteView != null) {
            outRect = new Rect(deleteView.getLeft(), deleteView.getTop(),
                deleteView.getRight(), deleteView.getBottom());
        } else {
            outRect = new Rect(0, 0, 0, 0);
        }
    }

    private static float adjustAngle(float degrees) {
        if (degrees > 180.0f) {
            degrees -= 360.0f;
        } else if (degrees < -180.0f) {
            degrees += 360.0f;
        }
        return degrees;
    }

    private static void move(Component view, TransformInfo info) {
        // computeRenderOffset(view, info.pivotX, info.pivotY);
        //  adjustTranslation(view, info.deltaX, info.deltaY);
        float scale = view.getScaleX() * info.deltaScale;
        scale = Math.max(info.minimumScale, Math.min(info.maximumScale, scale));
        view.setScaleX(scale);
        view.setScaleY(scale);
//        float rotation = adjustAngle(view.getRotation() + info.deltaAngle);
        // view.setRotation(rotation);
    }

    private  void adjustTranslation(Component view, float deltaX, float deltaY) {
        view.setTranslationX(mTranX + deltaX);
        view.setTranslationY(mTranY + deltaY);
    }

    private static void computeRenderOffset(Component view, float pivotX, float pivotY) {
        if (view.getPivotX() == pivotX && view.getPivotY() == pivotY) {
            return;
        }
        float[] prevPoint = {0.0f, 0.0f};
//        Matrix matrix = new Matrix();
        // view.getMatrix().mapPoints(prevPoint);
        //matrix.mapPoints(prevPoint);
        view.setPivotX(pivotX);
        view.setPivotY(pivotY);
        float[] currPoint = {0.0f, 0.0f};
        //  view.getMatrix().mapPoints(currPoint);
        //  matrix.mapPoints(currPoint);
        float offsetX = currPoint[0] - prevPoint[0];
        float offsetY = currPoint[1] - prevPoint[1];

        view.setTranslationX(view.getTranslationX() - offsetX);
        view.setTranslationY(view.getTranslationY() - offsetY);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        mScaleGestureDetector.onTouchEvent(component, event);
        mGestureListener.onTouchEvent(event);

        if (!isTranslateEnabled) {
            return true;
        }
        int action = event.getAction();
        MmiPoint m = event.getPointerScreenPosition(0);
//        int location[] = component.getLocationOnScreen();
        int x = (int) m.getX();
        int y = (int) m.getY();
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                mPointerStart[0] = event.getPointerScreenPosition(0);
                componentTranslation[0] = component.getTranslationX();
                componentTranslation[1] = component.getTranslationY();

//                MmiPoint ms = event.getPointerScreenPosition(0);
                //mPrevX = ms.getX();
                //mPrevY = ms.getY();
                mTranX = component.getTranslationX();
                mTranY = component.getTranslationY();
//                float mPrevRawX = event.getPointerScreenPosition(0).getX();
//                float mPrevRawY = event.getPointerScreenPosition(0).getY();
                mActivePointerId = event.getPointerId(0);
                if (deleteView != null) {
                    deleteView.setVisibility(Component.VISIBLE);
                }
                if (component.getComponentParent() != null && (component.getComponentParent() instanceof ComponentContainer)) {
                    ComponentContainer container = (ComponentContainer) component.getComponentParent();
                    container.moveChildToFront(component);
                }
                firePhotoEditorSDKListener(component, true);
                break;
            case TouchEvent.OTHER_POINT_DOWN:
                mPointerStart[0] = event.getPointerScreenPosition(0);
                mPointerStart[1] = event.getPointerScreenPosition(1);
                break;
            case TouchEvent.POINT_MOVE:
                if (event.getPointerCount() == 1) {
                    mPointerEnd[0] = event.getPointerScreenPosition(0);
                    Float[] mPointOneMove = getPointMoveXY(mPointerStart[0].getX(), mPointerStart[0].getY()
                        , mPointerEnd[0].getX(), mPointerEnd[0].getY());
                    setPointScalingDisplacement(component, mPointOneMove);
                } else if (event.getPointerCount() > 1) {
                    mPointerEnd[0] = event.getPointerScreenPosition(0);
                    mPointerEnd[1] = event.getPointerScreenPosition(1);
                    setPointScalingDisplacement(component, mPointerStart, mPointerEnd);
                }
                break;
            case TouchEvent.CANCEL:
                mActivePointerId = INVALID_POINTER_ID;
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                mActivePointerId = INVALID_POINTER_ID;
                componentTranslation[0] = component.getTranslationX();
                componentTranslation[1] = component.getTranslationY();
                if (deleteView != null && isViewInBounds(deleteView, x, y)) {
                    if (onMultiTouchListener != null) {
                        onMultiTouchListener.onRemoveViewListener(component);
                    }
                } else if (!isViewInBounds(photoEditImageView, x, y)) {
                    changeInBounds(component);
                }
                if (deleteView != null) {
                    deleteView.setVisibility(Component.HIDE);
                }
                firePhotoEditorSDKListener(component, false);
                break;
            case TouchEvent.OTHER_POINT_UP:
                int pointerIndexPointerUp = (action & event.getIndex()) >> event.getIndex();
                int pointerId = event.getPointerId(pointerIndexPointerUp);
                if (pointerId == mActivePointerId) {
                    int newPointerIndex = pointerIndexPointerUp == 0 ? 1 : 0;
//                    MmiPoint otherM = event.getPointerPosition(newPointerIndex);
                    //mPrevX = otherM.getX() - location[0];
                    //mPrevY = otherM.getY() - location[1];
                    mActivePointerId = event.getPointerId(newPointerIndex);
                }
                break;
            default:
                break;
        }
        return true;
    }

    public static void showDebugLog(String str) {
        System.out.println(str);
    }

    /**
     * 使用动画将文字位置还原
     *
     * @param component 控件
     */
    private void changeInBounds(Component component) {
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE);
        animatorValue.setDuration(300);
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                component.setTranslationY(component.getTranslationY() - component.getTranslationY() * v);
            }
        });
        animatorValue.start();
    }

    private void firePhotoEditorSDKListener(Component view, boolean isStart) {
        Object viewTag = view.getTag();
        if (mOnPhotoEditorListener != null && viewTag != null && viewTag instanceof ViewType) {
            if (isStart) {
                mOnPhotoEditorListener.onStartViewChangeListener(((ViewType) view.getTag()));
            } else {
                mOnPhotoEditorListener.onStopViewChangeListener(((ViewType) view.getTag()));
            }
        }
    }

    private boolean isViewInBounds(Component view, int x, int y) {
        getDrawingRect(view, outRect);
        location = view.getLocationOnScreen();
        outRect.offset(location[0], location[1]);
        return contains(outRect, x, y);
    }

    public boolean contains(Rect rect, int x, int y) {
        return rect.left < rect.right && rect.top < rect.bottom
            && x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
    }

    public void getDrawingRect(Component view, Rect outRect) {
        int mScrollX = view.getScrollValue(Component.AXIS_X);
        int mScrollY = view.getScrollValue(Component.AXIS_Y);
        outRect.left = mScrollX;
        outRect.top = mScrollY;
        outRect.right = mScrollX + (view.getRight() - view.getLeft());
        outRect.bottom = mScrollY + (view.getBottom() - view.getTop());
    }

    void setOnMultiTouchListener(OnMultiTouchListener onMultiTouchListener) {
        this.onMultiTouchListener = onMultiTouchListener;
    }

    public Float[] getPointMoveXY(float startX, float startY, float endX, float endY) {
        Float[] moveXY = new Float[2];
        /**
         * 计算控件XY位移的值;
         * moveXY[0] 为X轴,负数向左移动,正数右移动
         * moveXY[1] 为Y轴,负数向下移动,正数上移动
         */
        moveXY[0] = endX - startX;
        moveXY[1] = endY - startY;
        return moveXY;
    }

    /**
     * 单指位移
     *
     * @param component
     * @param pointOne 手指1位移的XY轴变量 getPointMoveXY(float startX, float startY, float endX, float endY)
     */
    public void setPointScalingDisplacement(Component component, Float[] pointOne) {
        if (component == null || pointOne == null) {
            throw new NullPointerException("not find component or pointOne!!!");
        }
        component.setTranslationX(componentTranslation[0] + pointOne[0]);
        component.setTranslationY(componentTranslation[1] + pointOne[1]);
    }

    /**
     * 双指 位移 缩放
     *
     * @param component
     * @param mPointerStart 初始化双指坐标点
     * @param mPointerEnd 位移后双指坐标点
     */
    public void setPointScalingDisplacement(Component component, MmiPoint[] mPointerStart, MmiPoint[] mPointerEnd) {
        if (component == null || mPointerStart == null || mPointerEnd == null) {
            throw new NullPointerException("not find component or pointOne!!!");
        }

        // 双指中心点 pointCenter[0]为mPointerStart中心点,pointCenter[1]为mPointerEnd中心点
        Float[][] pointCenter = {new Float[2], new Float[2]};
        pointCenter[0] = getTwoPointCenter(mPointerStart[0], mPointerStart[1]);

        pointCenter[1] = getTwoPointCenter(mPointerEnd[0], mPointerEnd[1]);
        // 位移双指中心点
        setComponentTranslation(component, mPointerStart[0], mPointerStart[1], mPointerEnd[0], mPointerEnd[1]);

        // 缩放，初始化双指距离,和停止后双指的距离比
        setComponentZoom(component, mPointerStart[0], mPointerStart[1], mPointerEnd[0], mPointerEnd[1]);
        // 旋转
        setComponentRotation(component, mPointerStart[0], mPointerStart[1], mPointerEnd[0], mPointerEnd[1]);

    }

    //获取中心点
    public Float[] getTwoPointCenter(MmiPoint pointOne, MmiPoint pointTwo) {
        Float[] centerPoint = new Float[2];
        centerPoint[0] = (pointOne.getX() + pointTwo.getX()) / 2;
        centerPoint[1] = (pointOne.getY() + pointTwo.getY()) / 2;
        return centerPoint;
    }

    /**
     * 双指位移Veiw
     *
     * @param component
     * @param startPointOne 第二根手指按下时,第一根手指的坐标
     * @param startPointTwo 第二根手指按下时候的坐标
     * @param endPointOne 位移后第一根手指坐标
     * @param endPointTwo 位移后第二根手指的坐标
     */
    public void setComponentTranslation(Component component, MmiPoint startPointOne, MmiPoint startPointTwo, MmiPoint endPointOne, MmiPoint endPointTwo) {
        Float[][] centerPoint = new Float[2][2];
        centerPoint[0] = getTwoPointCenter(startPointOne, startPointTwo);
        centerPoint[1] = getTwoPointCenter(endPointOne, endPointTwo);
        component.setTranslationX(componentTranslation[0] + (centerPoint[1][0] - centerPoint[0][0]));
        component.setTranslationY(componentTranslation[1] + (centerPoint[1][1] - centerPoint[0][1]));
    }

    /**
     * 缩放Veiw
     *
     * @param component
     * @param startPointOne 第二根手指按下时,第一根手指的坐标
     * @param startPointTwo 第二根手指按下时候的坐标
     * @param endPointOne 位移后第一根手指坐标
     * @param endPointTwo 位移后第二根手指的坐标
     */
    public void setComponentZoom(Component component, MmiPoint startPointOne, MmiPoint startPointTwo, MmiPoint endPointOne, MmiPoint endPointTwo) {
        //TODO 每次都会初始化为原来大小.
        //计算X轴缩放
        float startX = Math.abs(startPointTwo.getX() - startPointOne.getX());
        float endX = Math.abs(endPointTwo.getX() - endPointOne.getX());
        float scaleFactorX = (1 + ((endX - startX) / component.getWidth()));
        //计算Y轴缩放
        float startY = Math.abs(startPointTwo.getY() - startPointOne.getY());
        float endY = Math.abs(endPointTwo.getY() - endPointOne.getY());
        float scaleFactorY = 1 + ((endY - startY) / component.getHeight());

        scaleX = (scaleFactorX) > maximumScale ? maximumScale : ((scaleFactorX) < minimumScale ? minimumScale : (scaleFactorX));
        component.setScaleX(scaleX);

        scaleY = (scaleFactorY) > maximumScale ? maximumScale : ((+scaleFactorY) < minimumScale ? minimumScale : (scaleFactorY));
        component.setScaleY(scaleY);
    }

    /**
     * 旋转Veiw
     *
     * @param component
     * @param startPointOne 第二根手指按下时,第一根手指的坐标
     * @param startPointTwo 第二根手指按下时候的坐标
     * @param endPointOne 位移后第一根手指坐标
     * @param endPointTwo 位移后第二根手指的坐标
     */
    private void setComponentRotation(Component component, MmiPoint startPointOne, MmiPoint startPointTwo, MmiPoint endPointOne, MmiPoint endPointTwo) {
        double startRotation = Math.atan2(startPointOne.getY() - startPointTwo.getY(), startPointOne.getX() - startPointTwo.getX()) * (180 / Math.PI);
        double endRotation = Math.atan2(endPointOne.getY() - endPointTwo.getY(), endPointOne.getX() - endPointTwo.getX()) * (180 / Math.PI);
        double relativeRotation = endRotation - startRotation;
        component.setRotation((float) relativeRotation);
    }

    private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        /* private float mPivotX;
         private float mPivotY;*/
        private Vector2D mPrevSpanVector = new Vector2D();

        @Override
        public boolean onScaleBegin(Component view, ScaleGestureDetector detector) {
            /*mPivotX = detector.getFocusX();
            mPivotY = detector.getFocusY();*/
            mPrevSpanVector.set(detector.getCurrentSpanVector());
            return mIsTextPinchZoomable;
        }

        @Override
        public boolean onScale(Component view, ScaleGestureDetector detector) {
            TransformInfo info = new TransformInfo();
            boolean isScaleEnabled = true;
            info.deltaScale = isScaleEnabled ? detector.getScaleFactor() : 1.0f;
            boolean isRotateEnabled = true;
            /*info.deltaAngle = isRotateEnabled ? Vector2D.getAngle(mPrevSpanVector, detector.getCurrentSpanVector()) : 0.0f;
            info.deltaX = isTranslateEnabled ? detector.getFocusX() - mPivotX : 0.0f;
            info.deltaY = isTranslateEnabled ? detector.getFocusY() - mPivotY : 0.0f;
            info.pivotX = mPivotX;
            info.pivotY = mPivotY;*/
            float minimumScale = 0.5f;
            info.minimumScale = minimumScale;
            float maximumScale = 10.0f;
            info.maximumScale = maximumScale;
            move(view, info);
            return !mIsTextPinchZoomable;
        }
    }

    private static class TransformInfo {
        /*  private float deltaX;
          private  float deltaY;*/
        float deltaScale;
        /*private float deltaAngle;
         private float pivotX;
         private float pivotY;*/
        float minimumScale;
        float maximumScale;
    }

    interface OnMultiTouchListener {
        void onEditTextClickListener(String text, int colorCode);

        void onRemoveViewListener(Component removedView);
    }

    interface OnGestureControl {
        void onClick();

        void onLongClick();
    }

    void setOnGestureControl(OnGestureControl onGestureControl) {
        mOnGestureControl = onGestureControl;
    }

    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(TouchEvent e1, TouchEvent e2, float distanceX, float distanceY) {
            return false;
        }

        @Override
        public boolean onSingleTapUp(TouchEvent e) {
            if (mOnGestureControl != null) {
                mOnGestureControl.onClick();
            }
            return true;
        }

        @Override
        public void onLongPress(TouchEvent e) {
            if (mOnGestureControl != null) {
                mOnGestureControl.onLongClick();
            }
        }
    }

    @Override
    public void onLongClicked(Component component) {
        mGestureListener.setMLongClick(true);
        if (mOnGestureControl != null) {
            mOnGestureControl.onLongClick();
        }
    }
}